/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import com.mxgraph.layout.hierarchical.mxHierarchicalLayout;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.ColToSupertype;
import ro.dta.idbi.ui.sql.SqlColumnCell;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

/**
 * UI for the Columns to supertype transformation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class ColToSupertypeDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 1702301152486492444L;

	private static final Logger LOGGER = Logger
			.getLogger("ro.dta.idbi.ui.patterns.ColToSupertypeDialog");

	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length < 2) {
			return false;
		}

		int tableCount = 0;
		String colName = null;
		// Check if all columns have the same name and there is a table selected
		// (the supertype)
		for (Object o : objects) {
			if (!(o instanceof ISqlColumn) && !(o instanceof ISqlTable)) {
				return false;
			}

			if (o instanceof ISqlColumn) {
				final ISqlColumn crtCol = (ISqlColumn) o;
				if (crtCol.isPK()) {
					return false;
				}
				if (colName == null) {
					colName = crtCol.getValue().toString();
				} else if (!crtCol.getValue().equals(colName)) {
					return false;
				}
			} else {
				tableCount++;
				if (tableCount > 1) {
					return false;
				}
			}
		}

		if (tableCount != 1) {
			return false;
		}

		// Check if all tables containing the selected columns have the selected
		// table as the supertype
		List<String> intersection = null;
		String tableName = "";
		for (Object o : objects) {
			if (o instanceof ISqlColumn) {
				final ISqlColumn crtCol = (ISqlColumn) o;
				final ISqlTable parentTbl = (ISqlTable) crtCol.getParent();
				final List<String> supertypes = parentTbl.getSupertypesNames();
				if (intersection == null) {
					intersection = supertypes;
				} else {
					for (String s1 : intersection) {
						if (!supertypes.contains(s1)) {
							intersection.remove(s1);
						}
					}
				}
			} else {
				tableName = ((ISqlTable) o).getValue().toString();
			}
		}

		return intersection.contains(tableName);
	}
	
	/**
	 * Column to table dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public ColToSupertypeDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given constructs.");
		}

		final StringBuilder cmd = new StringBuilder(ColToSupertype.COMMAND);
		cmd.append(" (");
		
		for (ISqlConstruct c : element) {
			if (c instanceof ISqlTable) {
				cmd.append("<<").append(c.getValue()).append(">>, ");
			}
		}
		
		for (ISqlConstruct c : element) {
			if (c instanceof ISqlColumn) {
				ISqlTable parentTbl = (ISqlTable) c.getParent();
				cmd.append("<<").append(parentTbl.getValue()).append(",");
				cmd.append(c.getValue()).append(">>, ");
			}
		}
		
		cmd.setLength(cmd.length() - 2);
		cmd.append(")");

		macroField.setText(cmd.toString());
	}

	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			SqlTableCell superTbl = null;
			for (ISqlConstruct c : element) {
				if (c instanceof ISqlTable) {
					superTbl = (SqlTableCell) ((SqlTableCell) c).clone();
					final mxGeometry newGeometry = new mxGeometry(5, 5, c.getGeometry().getWidth(),
							c.getGeometry().getHeight());
					superTbl.setStyle("SEL_TABLE");
					superTbl.setGeometry(newGeometry);
					superTbl.layout(graph);
				}
			}

			for (ISqlConstruct c : element) {
				if (c instanceof ISqlColumn) {
					final SqlTableCell parentTable = (SqlTableCell) ((SqlColumnCell) c).getParent();
					final SqlTableCell newTable = (SqlTableCell) parentTable.clone();
					final mxGeometry newGeometry = new mxGeometry(5, 5, c.getGeometry().getWidth(),
							c.getGeometry().getHeight());
					final ISqlColumn col = newTable.getColumn(c.getValue().toString());
					col.setStyle("SEL_COL");
					newTable.setGeometry(newGeometry);
					newTable.layout(graph);

					final SqlForeignKeyCell fk = new SqlForeignKeyCell(null, null, "", "", newTable,
							superTbl, "FK_EDGE");
					graph.addCell(fk, graph.getDefaultParent());
				}
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			SqlTableCell superTbl = null;
			for (ISqlConstruct c : element) {
				if (c instanceof ISqlTable) {
					superTbl = (SqlTableCell) ((SqlTableCell) c).clone();
					final mxGeometry newGeometry = new mxGeometry(5, 5, c.getGeometry().getWidth(),
							c.getGeometry().getHeight());
					superTbl.setGeometry(newGeometry);
				}
			}

			ISqlColumn col = null;
			for (ISqlConstruct c : element) {
				if (c instanceof ISqlColumn) {
					final SqlTableCell parentTable = (SqlTableCell) ((SqlColumnCell) c).getParent();
					final SqlTableCell newTable = (SqlTableCell) parentTable.clone();
					final mxGeometry newGeometry = new mxGeometry(5, 5, c.getGeometry().getWidth(),
							c.getGeometry().getHeight());
					col = newTable.getColumn(c.getValue().toString());
					newTable.removeColumn(col);
					newTable.setGeometry(newGeometry);
					newTable.relayout(graph);

					final SqlForeignKeyCell foreignKey = new SqlForeignKeyCell(null, null, "", "",
							newTable, superTbl, "FK_EDGE");
					graph.addCell(foreignKey, graph.getDefaultParent());
				}
			}

			final ISqlColumn col1 = (ISqlColumn) col.clone();
			superTbl.addColumn(col1);
			superTbl.relayout(graph);

		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			final mxHierarchicalLayout layout = new mxHierarchicalLayout(graph);
			layout.execute(graph.getDefaultParent());
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

}
